home *** CD-ROM | disk | FTP | other *** search
- /*
- RGBtoYUVInit.c - Sets up the tables for use by RGBtoYUV.
-
- MacTech Magazine Programmers' Challenge
- July, 1994
- Written by Robert A. Noll
-
- Copyright (c) 1994 Robert A. Noll
- */
-
- #include "RGBtoYUV.h"
-
- /*
- BigNum - A fixed point number type with 8 bits of
- intregral part and 88 bits of fractional part.
- */
- typedef struct {
- unsigned long val[3];
- } BigNum;
-
- typedef struct {
- BigNum y[256];
- BigNum g[256];
- BigNum b[256];
- } TempBlock;
-
- /*
- Multiply - Takes a BigNum and multiplies it by a
- number in the range 0 - 255. Result is put into 'ans'.
- */
- void Multiply(void* bits, unsigned char num, void* ans)
- {
- int x;
- unsigned short* bp = (unsigned short*)bits + 5;
- unsigned short* ap = (unsigned short*)ans + 5;
- unsigned long val = 0;
-
- for (x=6; x; --x,--bp,--ap) {
- val += (unsigned long)*bp * num;
- *ap = val;
- val >>= 16;
- }
- }
-
- /*
- Add - Adds two BigNums and returns the result in 'ans'
- */
- void Add(void* bits1, void* bits2, void* ans)
- {
- int x;
- unsigned short* bp1 = (unsigned short*)bits1 + 5;
- unsigned short* bp2 = (unsigned short*)bits2 + 5;
- unsigned short* ap = (unsigned short*)ans + 5;
- unsigned long val = 0;
-
- for (x=6; x; --x,--bp1,--bp2,--ap) {
- val += (unsigned long)*bp1 + *bp2;
- *ap = val;
- val >>= 16;
- }
- }
-
- /*
- RGBtoYUVInit - Sets up all the tables used by the
- 'RGBtoYUV' function.
-
- • Since accuracy is a concern I use my own number
- format and routines to calculate tables. It turns
- out that FP routines are just as accurate but my
- stuff is 3x faster.
-
- About calculations in general:
-
- • A table with all 16M values for each of Y, U, and
- V would be nice but, at 48MB, would be way over
- 1MB limit. With minor calcs in 'RGBtoYUV' I need
- two 64K entry tables for each of U and V. At full
- precision they would be 768K each. They get
- reduced greatly later. The Y table I break into
- two tables: one for the R calcs and one for the
- G and B combination. At full precision they would
- take 3K and 768K respectivly. They get reduced
- later also. At this point we are a lot closer to
- the 1MB limit.
-
- About Y calculations:
-
- • 24 bits of fractional precision for the two tables
- was found to give proper results. This was found
- by checking the result of rounding the full
- precision result against the rounding of the sum
- of the two table entries for EVERY one of the 16M
- combinations. This allows us to represent our
- table entries in tables of 'unsigned long's. Table
- sizes are thus reduced to 1K and 256K.
- • Rounding is handled by adding
- .7FFFFFFFFFFFFFFFFFFFFF to the 'gb' table values.
- This allows RGBtoYUV to just truncate its result.
- • When R, G, and B are all equal then Y is the same
- number. With the tables that are built the
- checking for such a limited case (256 out of
- 16,777,216) just added space. (To calc 16M values
- takes a little over 2 minutes)
-
- About U and V in general:
-
- • Since B and R are multipied by .5 for these calcs
- we can except them as 7 bits of integral part and
- 1 bit of fraction.
- • Since the B or R calculation will always result in
- some number X.0 or X.5. This enables us to do all
- rounding in the table values. Also we are able to
- store the table numbers in the same 7.1 bit format.
- • If rounding is always down then RGBtoYUV can just
- truncate after the subtraction. If rounding is
- to be toward zero then special handling is needed.
-
- About V calculations:
-
- • Because of the number of decimal places and the
- fact that the multipliers add up to .50000001 the
- G and B combination will never result in exactly
- X.0 or X.5. Thus for V we don't need to worry
- about whether -0.5 rounds up or down.
-
- About U calculations:
-
- • The R and G combination values will result in an
- X.0 or X.5 result when R and G are equal.
- • If rounding is always down (.5 -> 0 and -.5 -> -1)
- then truncation after the calculation is all that
- is needed.
- • If rounding is always towards zero (.5 -> 0 and
- -.5 -> 0) then RGBtoYUV will add .5 and then
- truncate when R and G are equal. All other cases
- will still be handled with just truncation.
- */
- void *RGBtoYUVInit(void)
- {
- short r,g,b;
-
- BigNum mat[] = {
- { 0x004C8B43, 0x95810624, 0xDD2F1A9F },/*.29900000*/
- { 0x009645A1, 0xCAC08312, 0x6E978D4F },/*.58700000*/
- { 0x001D2F1A, 0x9FBE76C8, 0xB4395810 },/*.11400000*/
- { 0x002B3246, 0xA4293F94, 0x6A85AFD7 },/*.16873590*/
- { 0x0054CDB9, 0x5BD6C06B, 0x957A5028 },/*.33126410*/
- { 0x00800000, 0x00000000, 0x00000000 },/*.50000000*/
- { 0x00800000, 0x00000000, 0x00000000 },/*.50000000*/
- { 0x006B2F1C, 0x4D3DA074, 0x7F2DDD88 },/*.41868760*/
- { 0x0014D0E3, 0xDDB57D4F, 0xE1EA9636 },/*.08131241*/
- { 0x007FFFFF, 0xFFFFFFFF, 0xFFFFFFFF } /*.49999999*/
- };
- unsigned long *rp, *gbp;
- BigNum *gp, *bp, *yp;
- BigNum yt, ut, vt, xt;
- unsigned char *up, *vp;
- PrivateBlock *pb;
- TempBlock *tb;
-
- pb = (PrivateBlock*)NewPtr(sizeof(PrivateBlock));
- if (pb==0) {
- DebugStr("\pOut of Memory");
- return;
- }
- pb->rp = pb->r;
- pb->gbp = pb->gb;
- pb->up = pb->u;
- pb->vp = pb->v;
-
- tb = (TempBlock*)NewPtr(sizeof(TempBlock));
- if (tb==0) {
- DebugStr("\pOut of Memory");
- return;
- }
-
- /*
- r-values never = x.0 or x.5
- */
- rp = pb->rp;
- for (r=0; r<256; ++r,++rp) {
- Multiply(&mat[0],r,&xt);
- *rp = *((unsigned long*)&xt);
- }
-
- /*
- This method same as 0-255, 0-255 method.
-
- My calc routines equal SANE routines and are 3 times
- faster.
-
- u-values are never == x.0 or x.5
- v-values are never == x.0 or x.5
- */
- yp = tb->y;
- gp = tb->g;
- bp = tb->b;
- gbp = pb->gbp;
- up = pb->up;
- vp = pb->vp;
- for (b=0; b<256; ++b,++yp,++gp,++bp,++gbp,++up,++vp) {
- Multiply(&mat[2],b,yp);
- Add(yp,&mat[9],yp);
- *gbp = *((unsigned long*)yp);
- Multiply(&mat[4],b,gp);
- /*Add(gp,&mat[9],gp);*/
- *up = *((unsigned short*)gp) >> 7;
- Multiply(&mat[8],b,bp);
- /*Add(bp,&mat[9],bp);*/
- *vp = *((unsigned short*)bp) >> 7;
- }
- for (g=1; g<256; ++g) {
- Multiply(&mat[1],g,&yt);
- Add(&yt,&mat[9],&xt);
- *gbp = *((unsigned long*)&xt);
- ++gbp;
- Multiply(&mat[3],g,&ut);
- /*Add(&ut,&mat[9],&xt);*/
- *up = *((unsigned short*)&ut) >> 7;
- ++up;
- Multiply(&mat[7],g,&vt);
- /*Add(&vt,&mat[9],&xt);*/
- *vp = *((unsigned short*)&vt) >> 7;
- ++vp;
- yp = &tb->y[1];
- gp = &tb->g[1];
- bp = &tb->b[1];
- for (b=1; b<256;
- ++b,++gbp,++yp,++up,++gp,++vp,++bp) {
- Add(&yt,yp,&xt);
- *gbp = *((unsigned long*)&xt);
- if (g==b) {
- *up = b;
- *vp = b;
- }
- else {
- Add(&ut,gp,&xt);
- *up = *((unsigned short*)&xt) >> 7;
- Add(&vt,bp,&xt);
- *vp = *((unsigned short*)&xt) >> 7;
- }
- }
- }
-
- if (tb!=0)
- DisposPtr((Ptr)tb);
-
- pb->sig = 'RNCS';
- return pb;
- }
-